package redis

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/go-redis/redis/v8"
    "go.uber.org/zap"
)

type RedisDB struct {
    cfg *Config

    logger *zap.Logger
}

func New() *RedisDB {
    x := &RedisDB{}
    x.cfg = DefaultConfig()
    return x
}

func (s *RedisDB) WithConfig(c *Config) *RedisDB {
    s.cfg = c
    return s
}

func (s *RedisDB) Address(addr string) *RedisDB {
    s.cfg.Address = addr
    return s
}

func (s *RedisDB) UserName(u string) *RedisDB {
    s.cfg.Username = u
    return s
}

func (s *RedisDB) Password(pass string) *RedisDB {
    s.cfg.Password = pass
    return s
}

func (s *RedisDB) DB(n int) *RedisDB {
    s.cfg.DB = n
    return s
}

func (s *RedisDB) WithLogger(l *zap.Logger) *RedisDB {
    s.logger = l
    return s
}

func (s *RedisDB) AssertConnected() *RedisDB {
    rds := s.GoRedis()
    conn := func() bool {
        ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
        defer cancel()
        result, err := rds.Ping(ctx).Result()
        if err != nil {
            err_str := fmt.Sprintf("redis server %s: %s", s.cfg.Address, err.Error())
            if s.logger == nil {
                log.Println(err_str)
            } else {
                s.logger.Warn(err_str)
            }
            return false
        }
        if result != "PONG" {
            err_str := fmt.Sprintf("connect redis server %s failed", s.cfg.Address)
            if s.logger != nil {
                s.logger.Warn(err_str)
            }
            return false
        }
        return true
    }

    for {
        if conn() {
            break
        }
        time.Sleep(time.Second)
    }
    _ = rds.Close()
    return s
}

func (s *RedisDB) GoRedis() *redis.Client {
    rdb := redis.NewClient(&redis.Options{
        Addr:            s.cfg.Address,
        Username:        s.cfg.Username,
        Password:        s.cfg.Password,
        DB:              s.cfg.DB,
        MinRetryBackoff: -1,
        MaxRetryBackoff: -1,
    })
    return rdb
}
